using System;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using BI.Commons.OracleRoleProvider;
using BankInvest.InstrumentEditor.Controllers;
using BankInvest.InstrumentEditor.Domain.Entities;
using BankInvest.InstrumentEditor.Domain.Repositories;
using BankInvest.InstrumentEditor.NHibernate;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Connection;
using NHibernate.Context;
using NHibernate.Linq;
using NHibernate.Tool.hbm2ddl;
using StructureMap;
using StructureMap.Pipeline;

namespace BankInvest.InstrumentEditor
{
    public abstract class AppConfigurator
    {
        public void Configure()
        {
            ObjectFactory.Initialize(
               init =>
               {
                   init.For<IInstrumentEditorView>()
                       .Use<InstrumentEditorForm>();

                   init.For<ISessionFactory>()
                       .Use( CreateSessionFactory );

                   init.For<IUnitOfWork>()
                       .LifecycleIs( Lifecycles.GetLifecycle( InstanceScope.ThreadLocal ) )
                       .Use<UnitOfWork>();

                   init.For<ISession>()
                       .Use( CreateSession );

                   init.For<IInstrumentRepository>()
                       .Use<InstrumentRepository>();

                   init.For<IInstrumentTypeRepository>()
                       .Use<InstrumentTypeRepository>();
               } );
        }

        protected abstract ISessionFactory CreateSessionFactory();

        protected abstract ISession CreateSession( IContext context );
    }

    public class OracleAppConfigurator : AppConfigurator
    {
        protected override ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
               .Database( OracleDataClientConfiguration
                           .Oracle10
                           .AdoNetBatchSize( 500 )
                           .ConnectionString( String.Empty ) /* needed dummy */
                           .Provider<BIConnectionProvider>()
                           .DefaultSchema( "CI" ) )
               .Mappings( m => m.FluentMappings.AddFromAssemblyOf<InstrumentType>() )
               .BuildSessionFactory();
        }

        protected override ISession CreateSession(IContext context)
        {
            var unitOfWork = context.GetInstance<IUnitOfWork>();

            return unitOfWork.CurrentSession;
        }

        public class BIConnectionProvider : DriverConnectionProvider
        {
            public override IDbConnection GetConnection()
            {
                var oracleRoleProvider = new OracleRoleProvider();
                var dbConnection = Driver.CreateConnection();
                return oracleRoleProvider.SetUserRoles( dbConnection );
            }
        }
    }

    public class SqliteInMemoryAppConfigurator : AppConfigurator
    {
        private Configuration _nhConfig;

        protected override ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure().Database( SQLiteConfiguration.Standard.InMemory )
                .Mappings( m => m.FluentMappings.AddFromAssemblyOf<Instrument>() )
                .ExposeConfiguration( c =>
                {
                    c.Properties.Add( "current_session_context_class",
                                      typeof( ThreadStaticSessionContext ).FullName );
                    _nhConfig = c;
                } )
                .BuildSessionFactory();
        }

        protected override ISession CreateSession(IContext context)
        {
            var unitOfWork = context.GetInstance<IUnitOfWork>();

            var session = unitOfWork.CurrentSession;

            new SchemaExport( _nhConfig )
                .Execute( script: false, export: true, justDrop: false,
                         connection: session.Connection, exportOutput: null );

            AddTestDataTo( session );

            unitOfWork.Commit();

            return session;
        }

        private static void AddTestDataTo( ISession session )
        {
            TestEntities.InstrumentTypes.ForEach( it => session.Save( it ) );

            session.Save( new Instrument
            {
                Id = "890898",
                Name = "6% Kommunekredit 47s.A 2006",
                IsActive = true,
                Type = new InstrumentType( 1 )
            } );
        }

        internal static class TestEntities
        {
            public static readonly IEnumerable<InstrumentType> InstrumentTypes =
                new[]
                    {
                        new InstrumentType(0, "Undefined"),
                        new InstrumentType(1, "Bond"),
                        new InstrumentType(2, "Redeemed bond"),
                        new InstrumentType(3, "Option"),
                        new InstrumentType(4, "Currency forward (CI)"),
                        new InstrumentType(5, "Currency (spot)"),
                        new InstrumentType(6, "Settlement account"),
                        new InstrumentType(7, "Bonus share"),
                        new InstrumentType(8, "Future"),
                        new InstrumentType(9, "Index"),
                        new InstrumentType(10, "Certificate"),
                        new InstrumentType(11, "Convertible bond"),
                        new InstrumentType(13, "Mortgage deed"),
                        new InstrumentType(14, "Forward"),
                        new InstrumentType(15, "Warrant"),
                        new InstrumentType(16, "Currency future"),
                        new InstrumentType(17, "Currency forward"),
                        new InstrumentType(18, "Currency option"),
                        new InstrumentType(19, "Equity"),
                        new InstrumentType(20, "Skyldig Skat"),
                        new InstrumentType(21, "Excoupon Correction"),
                        new InstrumentType(22, "Bond Future"),
                        new InstrumentType(23, "Credit Default Swap"),
                        new InstrumentType(24, "Recovery Rate"),
                        new InstrumentType(25, "Convertible Loan"),
                        new InstrumentType(26, "Interest Rate Swap"),
                        new InstrumentType(27, "Swaption"),
                        new InstrumentType(28, "Skyldig Profit Fee(Day)"),
                        new InstrumentType(29, "Skyldig Profit Fee(Akk)"),
                        new InstrumentType(30, "Bond Forward"),
                        new InstrumentType(31, "Warrant Anti-dillution"),
                        new InstrumentType(32, "Manual Derivative"),
                        new InstrumentType(33, "Forward Rate Agreement"),
                        new InstrumentType(34, "Equity Portfolio SWAP"),
                        new InstrumentType(35, "P-Notes"),
                        new InstrumentType(36, "iTraxx"),
                        new InstrumentType(37, "Cash management"),
                        new InstrumentType(38, "External Investment Fund"),
                        new InstrumentType(39, "Internal Investment Fund")
                    };
        }
    }
}